﻿using System.Net;
using System.Net.Http.Headers;
using TaskManagerLib.MinimumWork;
using TaskManagerLib.Symbol;

namespace TaskManagerLib.Test;

public class ContinuesDownloadWork : MiniWork
{
    public override bool IsFinished()
    {
        return Status == WorkStatus.Finish;
    }

    public string Url { get; set; }

    public string FilePath { get; set; }

    public long BytesWritten { get; set; }

    public long BytesTotal { get; set; }

    public ContinuesDownloadWork(string url, string filepath,CancellationTokenSource source)
    {
        Url = url;
        FilePath = filepath;
        CancellationTokenSource = source;
    }


    public override bool Resume(CancellationTokenSource cancellationTokenSource)
    {
        if (InnerTask?.Status == TaskStatus.Running)
        {
            return false;
        }
        CancellationTokenSource = cancellationTokenSource;
        var check = false;
        Task.Run(async () =>
        {
            check = await Checkout();
        }).GetAwaiter().GetResult();
        if (CanPausedAndRunWithoutInformationLost() && check)
        {
            BuildContinuesTask();
        }
        else
        {
            BytesWritten = 0;
            BytesTotal = 0;
            Build();
        }
        Status = WorkStatus.Ready;
        return true;
    }

    public override async Task<bool> Checkout()
    {
        var existingBytes = 0L;
        if (File.Exists(FilePath))
        {
            existingBytes = new FileInfo(FilePath).Length;
        }
        try
        {
            using var client = new HttpClient();
            var request = new HttpRequestMessage(HttpMethod.Head, Url);

            var response = await client.SendAsync(request);
            var supportsRange = response.Headers.AcceptRanges.Contains("bytes");
            if (!supportsRange)
            {
                return false;
            }
        }
        catch (WebException ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }

        return existingBytes == BytesWritten;
    }



    public override bool CanPausedAndRunWithoutInformationLost()
    {
        return true;
    }
    public override async Task RecoverAction()
    {
        try
        {
            using var client = new HttpClient();
            var request = new HttpRequestMessage(HttpMethod.Get, Url);
            request.Headers.Range = new RangeHeaderValue(BytesWritten,null); // 设置起始字节位置

            using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            response.EnsureSuccessStatusCode();
            await using var stream = await response.Content.ReadAsStreamAsync();

            await using var fs = new FileStream(FilePath, FileMode.Append);
            var buffer = new byte[1024];
            int bytesRead;
            var lastNotificationTime = DateTime.MinValue;
            var notificationInterval = TimeSpan.FromMilliseconds(1024);
            Notification?.Invoke(new ProgressNotification()
            {
                Progress = Progress,
                Message = $"continue at {BytesWritten} Total {BytesTotal}"
            });
            while ((bytesRead =
                       await stream.ReadAsync(buffer,0,1024)) > 0)
            {
                if (CancellationTokenSource?.Token.IsCancellationRequested == true)
                {
                    await fs.FlushAsync();
                    fs.Close();
                    Notification?.Invoke(new ProgressNotification()
                    {
                        Progress = Progress,
                        Message = $"paused at {BytesWritten} Total {BytesTotal}"
                    });
                    Status = WorkStatus.Paused;
                    return;
                }

                var beforeWrite = BytesWritten;
                await fs.WriteAsync(buffer.AsMemory(0, bytesRead));
                BytesWritten += bytesRead;

                Progress = (double)BytesWritten / BytesTotal;
                if (DateTime.Now - lastNotificationTime < notificationInterval) continue;
                Notification?.Invoke(new ProgressNotification()
                {
                    Progress = Progress,
                    Message = $"write {beforeWrite} receive {bytesRead} now {BytesWritten} Total {BytesTotal}"
                });

                lastNotificationTime = DateTime.Now;
            }

            await fs.FlushAsync();
            fs.Close();

            Notification?.Invoke(new ProgressNotification()
            {
                Progress = Progress,
                Message = $"done now {BytesWritten} Total {BytesTotal}"
            });

            Result = FilePath;
            Status = WorkStatus.Finish;
        }
        catch (Exception e)
        {
            Status = WorkStatus.Except;
            Console.WriteLine(e);
        }
    }

    public override async Task Action()
    {
        try
        {
            using var client = new HttpClient();
            using var response = await client.GetAsync(Url, HttpCompletionOption.ResponseHeadersRead);
            response.EnsureSuccessStatusCode();

            await using var stream = await response.Content.ReadAsStreamAsync();
            var directory = Path.GetDirectoryName(FilePath);
            // 创建目录（如果不存在）
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            await using var fs = new FileStream(FilePath, FileMode.Create);
            var buffer = new byte[1024];
            int bytesRead;
            BytesTotal = response.Content.Headers.ContentLength??0;
            var lastNotificationTime = DateTime.MinValue;
            var notificationInterval = TimeSpan.FromMilliseconds(100);
            while ((bytesRead =
                       await stream.ReadAsync(buffer,0,1024)) > 0)
            {
                if (CancellationTokenSource?.Token.IsCancellationRequested == true)
                {
                    Status = WorkStatus.Paused;
                    await fs.FlushAsync();
                    Notification?.Invoke(new ProgressNotification()
                    {
                        Progress = Progress,
                        Message = $"paused at {BytesWritten} Total {BytesTotal}"
                    });
                    fs.Close();
                    return;
                }

                var beforeWrite = BytesWritten;
                await fs.WriteAsync(buffer.AsMemory(0, bytesRead));
                BytesWritten += bytesRead;

                Progress = (double)BytesWritten / BytesTotal;
                if (DateTime.Now - lastNotificationTime < notificationInterval) continue;
                Notification?.Invoke(new ProgressNotification()
                {
                    Progress = Progress,
                    Message = $"write {beforeWrite} receive {bytesRead} now {BytesWritten} Total {BytesTotal}"
                });

                lastNotificationTime = DateTime.Now;
            }

            await fs.FlushAsync();
            fs.Close();
            Notification?.Invoke(new ProgressNotification()
            {
                Progress = Progress,
                Message = $"done now {BytesWritten} Total {BytesTotal}"
            });

            Result = FilePath;
            Status = WorkStatus.Finish;
        }
        catch (Exception e)
        {
            Status = WorkStatus.Except;
            Console.WriteLine(e);
        }
    }
}